import XendRoot
xroot = XendRoot.instance()
import XendDB
+from XendError import XendError
import EventServer
eserver = EventServer.instance()
from xen.xend.server import SrvDaemon
daemon = SrvDaemon.instance()
-class XendConsoleInfo:
- """Console information record.
- """
-
- def __init__(self, console, dom1, port1, dom2, port2, conn=None):
- self.console = console
- self.dom1 = int(dom1)
- self.port1 = int(port1)
- self.dom2 = int(dom2)
- self.port2 = int(port2)
- self.conn = conn
- #self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2)
- self.id = str(port1)
-
- def __str__(self):
- s = "console"
- s += " id=%s" % self.id
- s += " src=%d.%d" % (self.dom1, self.port1)
- s += " dst=%d.%d" % (self.dom2, self.port2)
- s += " port=%s" % self.console
- if self.conn:
- s += " conn=%s:%s" % (self.conn[0], self.conn[1])
- return s
-
- def sxpr(self):
- sxpr = ['console',
- ['id', self.id],
- ['src', self.dom1, self.port1],
- ['dst', self.dom2, self.port2],
- ['port', self.console],
- ]
- if self.conn:
- sxpr.append(['connected', self.conn[0], self.conn[1]])
- return sxpr
-
- def connection(self):
- return self.conn
-
- def update(self, consinfo):
- conn = sxp.child(consinfo, 'connected')
- if conn:
- self.conn = conn[1:]
- else:
- self.conn = None
-
- def uri(self):
- """Get the uri to use to connect to the console.
- This will be a telnet: uri.
-
- return uri
- """
- host = socket.gethostname()
- return "telnet://%s:%s" % (host, self.console)
-
class XendConsole:
- dbpath = "console"
-
def __init__(self):
- self.db = XendDB.XendDB(self.dbpath)
- self.console = {}
- self.console_db = self.db.fetchall("")
- if xroot.get_rebooted():
- print 'XendConsole> rebooted: removing all console info'
- self.rm_all()
+ pass
eserver.subscribe('xend.domain.died', self.onDomainDied)
eserver.subscribe('xend.domain.destroy', self.onDomainDied)
- def rm_all(self):
- """Remove all console info. Used after reboot.
- """
- for (k, v) in self.console_db.items():
- self._delete_console(k)
-
- def refresh(self):
- consoles = daemon.consoles()
- cons = {}
- for consinfo in consoles:
- id = str(sxp.child_value(consinfo, 'id'))
- cons[id] = consinfo
- if id not in self.console:
- self._new_console(consinfo)
- for c in self.console.values():
- consinfo = cons.get(c.id)
- if consinfo:
- c.update(consinfo)
- else:
- self._delete_console(c.id)
-
def onDomainDied(self, event, val):
- dom = int(val)
- #print 'XendConsole>onDomainDied', 'event', event, "dom=", dom
- for c in self.consoles():
- #print 'onDomainDied', "dom=", dom, "dom1=", c.dom1, "dom2=", c.dom2
- if (c.dom1 == dom) or (c.dom2 == dom):
- 'XendConsole>onDomainDied', 'delete console dom=', dom
- ctrl = daemon.get_domain_console(dom)
- if ctrl:
- ctrl.close()
- self._delete_console(c.id)
-
- def sync(self):
- self.db.saveall("", self.console_db)
-
- def sync_console(self, id):
- self.db.save(id, self.console_db[id])
-
- def _new_console(self, consinfo):
- # todo: xen needs a call to get current domain id.
- dom1 = 0
- port1 = sxp.child_value(consinfo, 'local_port')
- dom2 = sxp.child_value(consinfo, 'domain')
- port2 = sxp.child_value(consinfo, 'remote_port')
- console = sxp.child_value(consinfo, 'console_port')
- info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2))
- info.update(consinfo)
- self._add_console(info.id, info)
- return info
-
- def _add_console(self, id, info):
- self.console[id] = info
- self.console_db[id] = info.sxpr()
- self.sync_console(id)
-
- def _delete_console(self, id):
- if id in self.console:
- del self.console[id]
- if id in self.console_db:
- del self.console_db[id]
- self.db.delete(id)
+ pass
def console_ls(self):
- self.refresh()
- return self.console.keys()
+ return [ c.console_port for c in self.consoles() ]
def consoles(self):
- self.refresh()
- return self.console.values()
+ return daemon.get_consoles()
def console_create(self, dom, console_port=None):
consinfo = daemon.console_create(dom, console_port=console_port)
- info = self._new_console(consinfo)
- return info
+ return consinfo
def console_get(self, id):
- self.refresh()
- return self.console.get(id)
-
- def console_delete(self, id):
- self._delete_console(id)
+ id = int(id)
+ for c in self.consoles():
+ if c.console_port == id:
+ return c
+ return None
def console_disconnect(self, id):
- id = int(id)
- daemon.console_disconnect(id)
+ console = self.console_get(id)
+ if not console:
+ raise XendError('Invalid console id')
+ console.disconnect()
def instance():
global inst
xroot = XendRoot.instance()
import XendDB
import XendDomainInfo
-import XendConsole
import XendMigrate
import EventServer
from XendError import XendError
"""Table of domain info indexed by domain id."""
domain = {}
- """Table of configs for domain restart, indexed by domain id."""
+ """Table of domains to restart, indexed by domain id."""
restarts = {}
"""Table of delayed calls."""
schedule = {}
def __init__(self):
- self.xconsole = XendConsole.instance()
# Table of domain info indexed by domain id.
self.db = XendDB.XendDB(self.dbpath)
self.domain_db = self.db.fetchall("")
deferred.addCallback(fn)
return deferred
+ def domain_restart(self, dominfo):
+ """Restart a domain.
+
+ @param dominfo: domain object
+ @return: deferred
+ """
+ deferred = dominfo.restart()
+ def fn(dominfo):
+ self._add_domain(dominfo.id, dominfo)
+ return dominfo
+ deferred.addCallback(fn)
+ return deferred
+
def domain_configure(self, id, config):
"""Configure an existing domain. This is intended for internal
use by domain restore and migrate.
if reason == 'halt':
self.domain_restart_cancel(id)
else:
- self.domain_restart_schedule(id, reason, set=1)
+ self.domain_restart_schedule(id, reason, force=1)
eserver.inject('xend.domain.shutdown', [id, reason])
if reason == 'halt':
reason = 'poweroff'
self.refresh_schedule()
return val
- def domain_restart_schedule(self, id, reason, set=0):
+ def domain_restart_schedule(self, id, reason, force=0):
"""Schedule a restart for a domain if it needs one.
@param id: domain id
@param reason: shutdown reason
"""
- log.debug('domain_restart_schedule> %s %s %d', id, reason, set)
+ log.debug('domain_restart_schedule> %s %s %d', id, reason, force)
dominfo = self.domain.get(id)
if not dominfo:
return
if id in self.restarts:
return
- if set and reason == 'reboot':
- dominfo.restart_mode = XendDomainInfo.RESTART_ALWAYS
- restart = dominfo.restart_needed(reason)
+ restart = (force and reason == 'reboot') or dominfo.restart_needed(reason)
if restart:
- # Avoid multiple restarts.
- dominfo.restart_mode = XendDomainInfo.RESTART_NEVER
- self.restarts[id] = dominfo.config
+ dominfo.restarting()
+ self.restarts[id] = dominfo
log.info('Scheduling restart for domain: id=%s name=%s', id, dominfo.name)
self.domain_restarts_schedule()
@param id: domain id
"""
- dominfo = self.domain.get(id)
+ dominfo = self.restarts.get(id)
if dominfo:
- dominfo.restart_mode = XendDomainInfo.RESTART_NEVER
- if id in self.restarts:
+ dominfo.restart_cancel()
del self.restarts[id]
def domain_restarts(self):
if id in self.domain:
# Don't execute restart for domains still running.
continue
- config = self.restarts[id]
+ dominfo = self.restarts[id]
# Remove it from the restarts.
del self.restarts[id]
try:
- log.info('domain_restarts> restart: id=%s config=%s', id, str(config))
+ log.info('domain_restarts> restart: id=%s config=%s', id, str(dominfo.config))
def cbok(dominfo):
log.info('Restarted domain %s as %s', id, dominfo.id)
self.domain_unpause(dominfo.id)
def cberr(err):
log.exception("Delayed exception restarting domain")
- deferred = self.domain_create(config)
+ deferred = self.domain_restart(dominfo)
deferred.addCallback(cbok)
deferred.addErrback(cberr)
except:
if reason == 'halt':
self.domain_restart_cancel(id)
elif reason == 'reboot':
- self.domain_restart_schedule(id, reason, set=1)
+ self.domain_restart_schedule(id, reason, force=1)
val = self.final_domain_destroy(id)
self.refresh_schedule()
return val
RESTART_NEVER,
]
+STATE_RESTART_PENDING = 'pending'
+STATE_RESTART_BOOTING = 'booting'
+
def shutdown_reason(code):
"""Get a shutdown reason from a code.
else:
d = defer.Deferred()
d.callback(vm)
+ vm.recreate = 0
return d
def vm_restore(src, progress=0):
self.state = self.STATE_OK
#todo: set to migrate info if migrating
self.migrate = None
- #Whether to auto-restart
self.restart_mode = RESTART_ONREBOOT
+ self.restart_state = None
self.console_port = None
def setdom(self, dom):
s += " name=" + self.name
s += " memory=" + str(self.memory)
if self.console:
- s += " console=" + self.console.id
+ s += " console=" + str(self.console.console_port)
if self.image:
s += " image=" + self.image
s += ""
devices have been released.
"""
if self.dom is None: return 0
+ if self.restart_state == STATE_RESTART_PENDING and self.console:
+ self.console.deregisterChannel()
chan = xend.getDomChannel(self.dom)
if chan:
log.debug("Closing channel to domain %d", self.dom)
memory = self.memory
name = self.name
cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
- dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
+ dom = self.dom or 0
+ dom = xc.domain_create(dom= dom, mem_kb= memory * 1024, name= name, cpu= cpu)
if dom <= 0:
raise VmError('Creating domain failed: name=%s memory=%d'
% (name, memory))
if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
err = buildfn(dom = dom,
image = kernel,
- control_evtchn = self.console.port2,
+ control_evtchn = self.console.getRemotePort(),
cmdline = cmdline,
ramdisk = ramdisk,
flags = flags)
if ramdisk and not os.path.isfile(ramdisk):
raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
self.init_domain()
- self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
+ if self.console:
+ self.console.registerChannel()
+ else:
+ self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
self.image = kernel
self.ramdisk = ramdisk
return reason == 'reboot'
return 0
+ def restart_cancel(self):
+ self.restart_state = None
+
+ def restarting(self):
+ self.restart_state = STATE_RESTART_PENDING
+
+ def restart(self):
+ try:
+ self.restart_state = STATE_RESTART_BOOTING
+ d = self.construct(self.config)
+ finally:
+ self.restart_state = None
+ return d
+
def configure_backends(self):
"""Set configuration flags if the vm is a backend for netif of blkif.
"""
self.xc = XendConsole.instance()
def op_disconnect(self, op, req):
- val = self.xc.console_disconnect(self.info.id)
+ val = self.xc.console_disconnect(self.info.console_port)
return val
def render_POST(self, req):
#self.ls()
req.write('<p>%s</p>' % self.info)
req.write('<p><a href="%s">Connect to domain %d</a></p>'
- % (self.info.uri(), self.info.dom2))
+ % (self.info.uri(), self.info.dom))
self.form(req)
req.write('</body></html>')
return ''
def form(self, req):
req.write('<form method="post" action="%s">' % req.prePathURL())
- if self.info.connection():
+ if self.info.connected():
req.write('<input type="submit" name="op" value="disconnect">')
req.write('</form>')
sxp.show(consoles, out=req)
else:
consoles = self.xconsole.consoles()
- consoles.sort(lambda x, y: cmp(x.id, y.id))
+ consoles.sort(lambda x, y: cmp(x.console_port, y.console_port))
req.write('<ul>')
for c in consoles:
- req.write('<li><a href="%s%s"> %s</a></li>' % (url, c.id, c))
+ cid = str(c.console_port)
+ req.write('<li><a href="%s%s"> %s</a></li>' % (url, cid, cid))
req.write('</ul>')
console_port = sxp.child_value(req, 'console_port')
if console_port:
console_port = int(console_port)
- resp = self.daemon.console_create(dom, console_port)
+ resp = self.daemon.console_create(dom, console_port).sxpr()
print name, resp
return resp
console = self.consoleCF.getInstanceByDom(dom)
if console is None:
console = self.consoleCF.createInstance(dom, console_port)
- return console.sxpr()
+ return console
def consoles(self):
return [ c.sxpr() for c in self.consoleCF.getInstances() ]
+ def get_consoles(self):
+ return self.consoleCF.getInstances()
+
def get_console(self, id):
return self.consoleCF.getInstance(id)
req.write('<p>%s</p>' % self.dom)
if self.dom.console:
cinfo = self.dom.console
- cid = cinfo.id
+ cid = str(cinfo.console_port)
#todo: Local xref: need to know server prefix.
req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
% (cid, cid))
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+import socket
+
from twisted.internet import reactor
from twisted.internet import protocol
self.listen()
def sxpr(self):
- val =['console',
- ['status', self.status ],
- ['id', self.idx ],
- ['domain', self.dom ],
- ['local_port', self.channel.getLocalPort() ],
- ['remote_port', self.channel.getRemotePort() ],
- ['console_port', self.console_port ] ]
+ val = ['console',
+ ['status', self.status ],
+ ['id', self.idx ],
+ ['domain', self.dom ] ]
+ val.append(['local_port', self.getLocalPort() ])
+ val.append(['remote_port', self.getRemotePort() ])
+ val.append(['console_port', self.console_port ])
if self.addr:
val.append(['connected', self.addr[0], self.addr[1]])
return val
+ def getLocalPort(self):
+ if self.channel:
+ return self.channel.getLocalPort()
+ else:
+ return 0
+
+ def getRemotePort(self):
+ if self.channel:
+ return self.channel.getRemotePort()
+ else:
+ return 0
+
+ def uri(self):
+ """Get the uri to use to connect to the console.
+ This will be a telnet: uri.
+
+ return uri
+ """
+ host = socket.gethostname()
+ return "telnet://%s:%d" % (host, self.console_port)
+
def ready(self):
return not (self.closed() or self.rbuf.empty())
Writes as much to the channel as it can.
"""
work = 0
- while not self.wbuf.empty() and self.channel.writeReady():
+ while self.channel and not self.wbuf.empty() and self.channel.writeReady():
msg = xu.message(CMSG_CONSOLE, 0, 0)
msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
work += self.channel.writeRequest(msg, notify=0)
if self.closed(): return -1
if conn != self.conn: return 0
self.wbuf.write(data)
- if self.produceRequests():
+ if self.channel and self.produceRequests():
self.channel.notify()
return 0
"""
if self.channel:
self.channel.deregisterDevice(self)
- del self.channel
+ self.channel = None
def produceRequests(self):
"""Produce any queued requests.
dom = int(sxp.child_value(dominfo, 'id'))
console_info = sxp.child(dominfo, 'console')
if console_info:
- console_port = int(sxp.child_value(console_info, 'port'))
+ console_port = int(sxp.child_value(console_info, 'console_port'))
else:
console_port = None